PHP 可以透過 include、include_once、require、require_once 來將檔案引入, 一般來說以 OOP 寫法習慣, 一個檔案一個 class, 所以如果我們要用到 Car.php 的 Car class, 我們必須引入 Car.php
// index.php
include "Car.php";
$car = new Car();
這樣沒有問題, 但是如果我們有很多 class 要用時, 都要一個一個手動引入, 實在是有違工程師初心, 最好是用到的時候表面上可以直接用, 實際上系統幫我們自動引入相關 php 檔案, PHP 提供了一些 spl_autoload 相關函數來讓我們可以完成自動引入的功能
<?php
/*
spl_autoload_register(?callable $callback = null, bool $throw = true, bool $prepend = false): bool
註冊一個函數到 spl_autoload 佇列, 如果佇列還沒啟用就啟用
$callback: 欲註冊的自動引入函數, 如果沒有提供, 會自動註冊 spl_autoload 函數, 這是系統預設的自動引入函數
$throw: 無法註冊成功時, 是否拋出異常
$prepend: 若為 true, 表示註冊的函數放入佇列頭部, false, 放入尾部
概念: 當使用到未被定義的 class 或 interface 時, PHP 引擎會通過自動引入函數來引入所需要的 class, interface
*/
// 假設 class 都放在 ./classes 目錄裡
// ./classes/Car.php
class Car
{
public function start()
{
echo "car is starting";
}
}
// index.php
function autoload($className) $filename;
{
$filename = __DIR__ . "/classes/" . $className . ".php";
if (is_readable($filename)) {
include $filename;
}
}
spl_autoload_register("autoload");
$car = new Car();
$car->start();
再搭配 namesapce 的使用可以, 實作出 PSR-4 的 Autoloader
<?php
// 自動引入類
class Psr4AutoLoad
{
// 這裡面存放命名空間映射
protected $maps = [];
public function __construct()
{
spl_autoload_register([$this, 'autoload']);
}
// 自己寫的自動引入
public function autoload($className)
{
// 完整的類名由命名空間名和類名組成
// 得到命名空間名, 根據命名空間名得到其目錄路徑
$pos = strrpos($className, '\\'); //找最右邊的'\'
$namespace = substr($className, 0, $pos);
// 得到類名
$realClass = substr($className, $pos + 1);
// 找到文件並且包含進來
$this->mapLoad($namespace, $realClass);
}
// 根據命名空間名得到目錄路徑並且拼接真正的文件全路徑
protected function mapLoad($namespace, $realClass)
{
if (array_key_exists($namespace, $this->maps)) {
$namespace = $this->maps[$namespace];
}
// 處理路徑
$namespace = rtrim(str_replace('\\/', '/', $namespace), '/') . '/';
// 拼接文件全路徑
$filePath = $namespace . $realClass . '.php';
// 將該文件包含進來即可
if (file_exists($filePath)) {
include $filePath;
} else {
die('該文件不存在');
}
}
// 給一個命名空間, 給一個路徑, 將命名空間和路徑保存到映射陣列中
public function addMaps($namespace, $path)
{
if (array_key_exists($namespace, $this->maps)) {
die('此命名空間已經映射過');
}
// 命名空間和路徑以鍵值對形式存放到陣列中
$this->maps[$namespace] = $path;
}
}
$autoloader = new Psr4AutoLoad();